﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Documents;
using System.Windows.Markup;
using System.Net;
using System.Xml.Linq;
using System.IO;


namespace WikiToXaml
{
    public class WikiToXamlConvertor
    {       
        private static Regex _b = new Regex("'''([^']+)'''");
        private static Regex _i = new Regex("''([^']+)''");
        private static Regex _a = new Regex(@"\[\[([^\[\]]+)\]\]");
        private static Regex _img = new Regex(@"[a-z]{3,}:([^\.]+\.[a-z]{3,})", RegexOptions.IgnoreCase);
        private static Regex _template = new Regex(@"\{\{[^\}]+\}\}", RegexOptions.IgnoreCase);
        private static Regex[] _delete = new Regex[] {  new Regex(@"(<!--[^<]*-->)", RegexOptions.Multiline),
                                                        new Regex(@"(<ref[^<]*</ref>)", RegexOptions.Multiline | RegexOptions.IgnoreCase),
                                                        new Regex(@"(<ref[^/]*/>)", RegexOptions.Multiline | RegexOptions.IgnoreCase),
                                                        new Regex(@"(<references[^/]*/>)", RegexOptions.Multiline | RegexOptions.IgnoreCase)};

        private static char[] _trimHeadings = new char[] {'=', ' '};

        private static bool _renderForSurface = false;

        private static string _resolveImageUrl = "http://{1}.wikipedia.org/w/api.php?action=query&prop=imageinfo&iiprop=url&iiurlwidth=180px&titles={0}&format=xml";

        public static bool RenderForSurface { get { return _renderForSurface; } set { _renderForSurface = value; } }

        public string ConvertToXaml(string wikiText)
        {
            foreach (Regex r in _delete)
            {
                MatchCollection mc = r.Matches(wikiText);

                foreach (Match m in mc)
                {
                    wikiText = wikiText.Replace(m.Groups[1].Value, "");
                }    
            }
            

            wikiText = wikiText.Replace("&nbsp;", " ");
            wikiText = wikiText.Replace("&", "&amp;");
            wikiText = wikiText.Replace(">", "&gt;");
            wikiText = wikiText.Replace("<", "&lt;");
            wikiText = wikiText.Replace("\"", "&quot;");
            

            StringBuilder sb = new StringBuilder();

            sb.Append("<FlowDocument xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"");
            sb.Append(" FontFamily=\"Calibri\"");
            sb.Append(" FontSize=\"12\"");
            if (_renderForSurface)
            {
                sb.Append(" xmlns:s=\"http://schemas.microsoft.com/surface/2008\"");
            }

            sb.Append(">");

            string[] lines = wikiText.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None);

            int linesCount = lines.Length;
            string line;


            for (int i = 0; i < linesCount; i++)
            {
                line = lines[i].Trim();

                if (line.Length == 0)
                {
                    continue;
                }

                line = ProcessInline(line);

                if (line.StartsWith("==="))
                {
                    sb.Append("<Paragraph FontWeight=\"Bold\" FontSize=\"14\">" + line.Trim(_trimHeadings) + "</Paragraph>");
                }
                else if (line.StartsWith("=="))
                {
                    sb.Append("<Paragraph FontWeight=\"UltraBold\" FontSize=\"16\">" + line.Trim(_trimHeadings) + "</Paragraph>");
                }
                else if (line.StartsWith(";") && lines[i + 1].StartsWith(":"))
                {
                    sb.Append(String.Format("<Paragraph><Run FontWeight=\"Bold\">{0}</Run><LineBreak/>{1}</Paragraph>",
                                            line.Substring(1).Trim(),
                                            lines[i + 1].Substring(1).Trim()));
                    i++;
                }
                else if (line.StartsWith("*"))
                {
                    sb.Append(CreateList(ref i, lines, 1));
                }
                else if (line.StartsWith("{{"))
                {
                    DeleteTemplate(ref i, lines);
                }
                else if (line.StartsWith("{|"))
                {
                    sb.Append(CreateTable(ref i, lines));
                }
                else if (line.Trim().ToLower().Equals("&lt;gallery&gt;"))
                {
                    sb.Append(CreateGallery(ref i, lines));
                }
                else
                {
                    sb.Append("<Paragraph>" + line + "</Paragraph>");
                }
                sb.Append(Environment.NewLine);
            }

            sb.Append("</FlowDocument>");

            return sb.ToString();
        }

        private string CreateGallery(ref int i, string[] lines)
        {
            StringBuilder sb = new StringBuilder();

            i++;

            while (!lines[i].Trim().Equals("&lt;/gallery&gt;"))
            {
                string[] parts = lines[i++].Split(new char[] { '|' }, 2);
                string imageUrl = ResolveImageUrl(parts[0]);
                if (imageUrl != null)
                {
                    sb.AppendFormat("<Paragraph><Image Stretch=\"None\" Source=\"{0}\" /></Paragraph>", imageUrl);
                }
                else
                {
                    continue;
                }

                if (parts.Length == 2)
                {
                    sb.Append("<Paragraph>" + ProcessInline(parts[1].Trim()) + "</Paragraph>");
                }
            }

            return sb.ToString();
        }

        private string CreateTable(ref int i, string[] lines)
        {
            string tableTitle = null;
            StringBuilder sb = new StringBuilder();

            sb.Append("<Table>");
            sb.AppendLine();

            sb.Append("<Table.Columns>{0}");
            sb.Append(Environment.NewLine);
            sb.Append("</Table.Columns>");
            sb.Append(Environment.NewLine);

            i++;
            if (lines[i].StartsWith("|+"))
            {
                tableTitle = lines[i].Substring(2).Trim();
                i++;
            }



            sb.Append("</Table>");
            return "";
        }

        public String Language { get; set; }

        private string CreateList(ref int lineNumber, string[] lines, int level)
        {            
            StringBuilder sb = new StringBuilder();
            sb.Append("<List>");
            while (lines[lineNumber].StartsWith("*"))
            {
                string line = lines[lineNumber].Substring(level);
                if (line.StartsWith("*"))
                {
                    sb.Remove(sb.Length - "</ListItem>".Length - 1, "</ListItem>".Length);
                    sb.Append(CreateList(ref lineNumber, lines, level+1));
                    sb.Append("</ListItem>");
                }
                else
                {
                    sb.Append(String.Format("<ListItem><Paragraph>{0}</Paragraph></ListItem>", ProcessInline(line)));
                }                
                lineNumber++;
            }
            sb.Append("</List>");
            return sb.ToString();
        }

        private void DeleteTemplate(ref int lineNumber, string[] lines)
        {
            do
            {
                lineNumber++;
            } while (!lines[lineNumber].Trim().StartsWith("}}") && !lines[lineNumber].Trim().EndsWith("}}"));
        }
        
        public FlowDocument ConvertToFlowDocument(string wikiText)
        {
            FlowDocument fd = (FlowDocument)XamlReader.Parse(ConvertToXaml(wikiText));
            fd.FontFamily = new System.Windows.Media.FontFamily("Calibri");
            fd.FontSize = 12.0;
            
            return fd;
        }

        private string ProcessInline(string line)
        {
            MatchCollection mc = _a.Matches(line);
            foreach (Match m in mc)
            {
                string[] parameters = m.Groups[1].Value.Split(new char[] { '|' });

                Match m1 = _img.Match(parameters[0]);

                if (m1.Success)
                {
                    string imageUrl = ResolveImageUrl(m.Groups[1].Value);
                    if (imageUrl != null)
                    {
                        line = line.Replace(m.Groups[0].Value, String.Format("<Image Stretch=\"None\" Source=\"{0}\" />", imageUrl));
                    }
                }
                else if (parameters.Length == 1 && !parameters[0].StartsWith("http"))
                {                    
                    if (_renderForSurface)
                    {
                        line = line.Replace(m.Groups[0].Value, String.Format("<InlineUIContainer BaselineAlignment=\"Center\"><s:SurfaceButton Tag=\"{0}\">{0}</s:SurfaceButton></InlineUIContainer>", parameters[0].Replace("'", "")));
                    }
                    else
                    {
                        line = line.Replace(m.Groups[0].Value, String.Format("<Hyperlink NavigateUri=\"{0}\">{0}</Hyperlink>", parameters));
                    }
                }
                else if (parameters.Length == 2)
                {
                    if (_renderForSurface)
                    {
                        line = line.Replace(m.Groups[0].Value, String.Format("<InlineUIContainer BaselineAlignment=\"Center\"><s:SurfaceButton Tag=\"{0}\">{1}</s:SurfaceButton></InlineUIContainer>", parameters[0], parameters[1].Replace("'", "")));
                    }
                    else
                    {
                        line = line.Replace(m.Groups[0].Value, String.Format("<Hyperlink NavigateUri=\"{0}\">{1}</Hyperlink>", parameters));
                    }
                }                
            }

            mc = _b.Matches(line);
            foreach (Match m in mc)
            {
                line = line.Replace(m.Groups[0].Value, "<Bold>" + m.Groups[1].Value + "</Bold>");
            }

            mc = _i.Matches(line);
            foreach (Match m in mc)
            {
                line = line.Replace(m.Groups[0].Value, "<Italic>" + m.Groups[1].Value + "</Italic>");
            }

            mc = _template.Matches(line);
            foreach (Match m in mc)
            {
                line = line.Replace(m.Groups[0].Value, "");
            }

            return line;
        }

        private string ResolveImageUrl(string imageName)
        {
            string imageUrl = null;

            WebClient w = new WebClient();
            XDocument d = XDocument.Parse(w.DownloadString(String.Format(_resolveImageUrl, Uri.EscapeUriString(imageName), this.Language)));

            XElement e = d.Descendants("ii").FirstOrDefault<XElement>();

            if (e != null && e.Attribute("thumburl") != null)
            {
                imageUrl = e.Attribute("thumburl").Value;
            }

            return imageUrl;
        }
    }
}
